#include <linux/vmalloc.h>
#include "rxe.h"
#include "rxe_loc.h"
#include "rxe_queue.h"

int rxe_cq_chk_attr(struct rxe_dev *rxe, struct rxe_cq *cq,
        int cqe, int comp_vector)//最后一个参数没什么用
{
    int count;

    /*判断是否小于0*/
    if(cqe <= 0) {
        pr_warn("cqe(%d) <= 0\n", cqe);
        goto err1;
    }

    /*判断是否大于cqe的最大个数*/
    if(cqe > rxe->attr.max_cqe) {
        pr_warn("cqe(%d) > max_cqe(%d)\n", cqe, rxe->attr.max_cqe);
        goto err1;
    }

    if(cq) {
        //得到当前cq队列的cqe个数
        count = queue_count(cq->queue, QUEUE_TYPE_TO_CLIENT);
        if (cqe < count) {
            pr_warn("cqe(%d) < current # elements in queue (%d)", cqe, count);
            goto err1;
        }
    }

    return 0;
err1:
    return -EINVAL;
}

static void rxe_send_complete(struct tasklet_struct *t)
{
    struct rxe_cq *cq = from_tasklet(cq, t, comp_task);
    unsigned long flags;

    spin_lock_irqsave(&cq->cq_lock, flags);
    if (cq->is_dying) {
        spin_unlock_irqrestore(&cq->cq_lock, flags);
        return;
    }
    spin_unlock_irqrestore(&cq->cq_lock, flags);

    //通知上层有cqe产生
    cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context);
}

int rxe_cq_from_init(struct rxe_dev *rxe, struct rxe_cq *cq, int cqe,
        int comp_vector, struct ib_udata *udata,
        struct rxe_create_cq_resp __user *uresp)
{
    int err;
    enum queue_type type;
    type = QUEUE_TYPE_TO_CLIENT;

    /*cq队列初始化*/
    cq->queue = rxe_queue_init(rxe, &cqe, sizeof(struct rxe_cqe), type);
    if(!cq->queue) {
        pr_warn("unable to create cq\n");
        return -ENOMEM;
    }

    /*创建mmap信息*/
    err = do_mmap_info(rxe, uresp ? &uresp->mi : NULL, udata,
            cq->queue->buf, cq->queue->buf_size, &cq->queue->ip);
    if (err) {
        vfree(cq->queue->buf);
        kfree(cq->queue);
        return err;
    }

    if (uresp)
        cq->is_user = 1;

    /*队列销毁标志*/
    cq->is_dying = false;

    /*设置生成cqe时上报应用层的回调函数*/
    tasklet_setup(&cq->comp_task, rxe_send_complete);

    spin_lock_init(&cq->cq_lock);
    cq->ibcq.cqe = cqe;

    return 0;
}

/*调整队列元素大小*/
int rxe_cq_resize_queue(struct rxe_cq *cq, int cqe,
        struct rxe_resize_cq_resp __user *uresp,
        struct ib_udata *udata)
{
    int err;

    err = rxe_queue_resize(cq->queue, (unsigned int *)&cqe,
            sizeof(struct rxe_cqe), udata,
            uresp ? &uresp->mi : NULL, NULL, &cq->cq_lock);
    if(!err)
        cq->ibcq.cqe = cqe;

    return err;
}

/*产生cqe*/
int rxe_cq_post(struct rxe_cq *cq, struct rxe_cqe *cqe, int solicited)
{
    struct ib_event ev;
    unsigned long flags;
    int full;
    void *addr;

    spin_lock_irqsave(&cq->cq_lock, flags);

    //首先判断队列释放满，因为下一步要进行wc的拷贝了，并且移动pidx,所以先判断有没有满*/
    full = queue_full(cq->queue, QUEUE_TYPE_TO_CLIENT);
    if(unlikely(full)) { //如果此时队列满了
        spin_unlock_irqrestore(&cq->cq_lock, flags);

        /*这里的流程没有太明白*/
        if (cq->ibcq.event_handler) {
            ev.device = cq->ibcq.device;
            ev.element.cq = &cq->ibcq;
            ev.event = IB_EVENT_CQ_ERR;
            cq->ibcq.event_handler(&ev, cq->ibcq.cq_context);
        }
        return -EBUSY;
    }

    /*得到当前cqe_index指向的首地址，然后进行拷贝*/
    addr = queue_producer_addr(cq->queue, QUEUE_TYPE_TO_CLIENT);
    memcpy(addr, cqe, sizeof(*cqe));

    /*pidx往下移动*/
    queue_advance_producer(cq->queue, QUEUE_TYPE_TO_CLIENT);
    spin_unlock_irqrestore(&cq->cq_lock, flags);

    /*是否采用事件上报的机制*/
    if ((cq->notify == IB_CQ_NEXT_COMP) ||
        (cq->notify == IB_CQ_SOLICITED && solicited)) {
        cq->notify = 0;
        tasklet_schedule(&cq->comp_task);
    }

    return 0;
}

/*销毁cq队列时调用*/
void rxe_cq_disable(struct rxe_cq *cq)
{
    unsigned long flags;

    spin_lock_irqsave(&cq->cq_lock, flags);
    cq->is_dying = true;
    spin_unlock_irqrestore(&cq->cq_lock, flags);
}

void rxe_cq_cleanup(struct rxe_pool_entry *arg)
{
    struct rxe_cq *cq = container_of(arg, typeof(*cq), pelem);
    /*释放cq队列缓冲区*/
    if (cq->queue)
        rxe_queue_cleanup(cq->queue);
}
